package com.xiaowen.redis;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 */
@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 默认过期时长为24小时，单位：秒
     */
    public final static long DEFAULT_EXPIRE = 60 * 60 * 24L;
    /**
     * 过期时长为1小时，单位：秒
     */
    public final static long HOUR_ONE_EXPIRE = 60 * 60 * 1L;
    /**
     * 过期时长为6小时，单位：秒
     */
    public final static long HOUR_SIX_EXPIRE = 60 * 60 * 6L;
    /**
     * 不设置过期时长
     */
    public final static long NOT_EXPIRE = -1L;

    /**
     * 定时任务过期时间 单位秒
     */
    public final static long TASK_EXPIRE = 10;

    public void set(String key, String value, long expire) {
        redisTemplate.opsForValue().set(key, value);
        if (expire != NOT_EXPIRE) {
            expire(key, expire);
        }
    }

    public void set(String key, Object value, long expire) {
        redisTemplate.opsForValue().set(key, value);
        if (expire != NOT_EXPIRE) {
            expire(key, expire);
        }
    }

    public void set(String key, String value) {
        set(key, value, DEFAULT_EXPIRE);
    }

    public Object get(String key, long expire) {
        if(StringUtils.isEmpty(key)){
            return null;
        }
        Object value = redisTemplate.opsForValue().get(key);
        if (expire != NOT_EXPIRE) {
            expire(key, expire);
        }
        return value;
    }

    public Object get(String key) {
        return get(key, NOT_EXPIRE);
    }

    public String getString(String key) {
        Object object = get(key, NOT_EXPIRE);
        if (null == object) {
            return null;
        }
        return object.toString();
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    public void delete(Collection<String> keys) {
        redisTemplate.delete(keys);
    }
    public void deleteKeysByPattern(String pattern) {
        List<String> keysToDelete = new ArrayList<>();
        Cursor<byte[]> cursor = redisTemplate.getConnectionFactory()
                .getConnection()
                .scan(ScanOptions.scanOptions().match(pattern).count(1000).build());

        while (cursor.hasNext()) {
            keysToDelete.add(new String(cursor.next()));
        }

        if (!keysToDelete.isEmpty()) {
            redisTemplate.delete(keysToDelete);
        }
    }
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    public Map<String, Object> hGetAll(String key) {
        HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations.entries(key);
    }

    public void hMSet(String key, Map<String, ?> map) {
        hMSet(key, map, DEFAULT_EXPIRE);
    }

    public void hMSet(String key, Map<String, ?> map, long expire) {
        redisTemplate.opsForHash().putAll(key, map);

        if (expire != NOT_EXPIRE) {
            expire(key, expire);
        }
    }

    public List<Object> hMGet(String key, Collection<Object> hashKeys) {
        return redisTemplate.opsForHash().multiGet(key, hashKeys);
    }

    public void zSet(String key, String field, int doubleValue) {
        redisTemplate.opsForZSet().add(key, field, doubleValue);
    }

    public void zSetIncrement(String key, String field) {
        redisTemplate.opsForZSet().incrementScore(key, field, 1);
    }

    public Set<Object> zSetList(String key) {
        Set<Object> sets = redisTemplate.opsForZSet().reverseRange(key, 0, 100);
        return sets;
    }

    public int getZset(String key, String field) {
        Double d = redisTemplate.opsForZSet().score(key, field);
        if (null == d) {
            return 0;
        }
        return d.intValue();
    }

    public void hSet(String key, String field, Object value) {
        hSet(key, field, value, DEFAULT_EXPIRE);
    }

    public void hSet(String key, String field, Object value, long expire) {
        redisTemplate.opsForHash().put(key, field, value);

        if (expire != NOT_EXPIRE) {
            expire(key, expire);
        }
    }

    public void expire(String key, long expire) {
        redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    public void hDel(String key, Object... fields) {
        redisTemplate.opsForHash().delete(key, fields);
    }

    public void leftPush(String key, Object value) {
        leftPush(key, value, DEFAULT_EXPIRE);
    }

    public void leftPush(String key, Object value, long expire) {
        redisTemplate.opsForList().leftPush(key, value);
        if (expire != NOT_EXPIRE) {
            expire(key, expire);
        }
    }

    public Object rightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 给key设置过期时间 秒
     *
     * @param key
     * @param time 单位秒
     * @return
     */
    public Object setExpire(String key, long time) {
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 最终加强分布式锁
     *
     * @param key          key值
     * @param expireSecond 过期时间
     * @return 是否获取到
     */
    public boolean setNX(String key, long expireSecond) {
        try {
           long expireSecondTime = expireSecond * 1000;
            String lock = key;
            // 利用lambda表达式
            Object lockObj = redisTemplate.execute((RedisCallback) connection -> {
                long expireAt = System.currentTimeMillis() + expireSecondTime;
                //设置
                Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
                if (acquire) {
                    //添加有效时间
                    setExpire(lock, expireSecond);
                }
                return acquire;
            });
            if (null == lockObj) {
                return false;
            }
            return (Boolean) lockObj;
        } catch (Exception e) {
            return false;
        }
    }

    public Set<Object> getZREVRange(String key) {
        Set<Object> list = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
        return list;
    }

    public void zSetOrUpdate(String data, Object parameter) {
        if (null == parameter || parameter.toString().length() == 0) {
            return;
        }
        String field = parameter.toString();
//      验证是否存在
        int score = getZset(data, field);
//     不存在 新增
        if (score == 0) {
            zSet(data, field, 1);
        } else {
            //     存在 分数增加
            zSetIncrement(data, field);
        }
    }

    public boolean exists(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    public int incrementInt(String key, int delta) {
        Long increment = redisTemplate.opsForValue().increment(key, delta);
        if(null == increment){
            return 0;
        }
        return increment.intValue();
    }

    /**
     * 列表中右侧添加数据
     *
     * @param key
     * @param step
     */
    public void setAddList(String key, Integer step) {
        ListOperations<String, Object> listOps = redisTemplate.opsForList();
        listOps.rightPush(key, step);
    }

    /**
     * 返回所有list
     *
     * @param key
     * @return
     */
    public List<String> getList(String key) {
        ListOperations<String, Object> listOps = redisTemplate.opsForList();
        List<Object> objects = listOps.range(key, 0, -1);
        List<String> list = new ArrayList<>();
        for(Object ob : objects){
            if(null != ob){
                list.add(ob.toString());
            }
        }
        return list;
    }

    public int getInteger(String key) {
        String ob = getString(key);
        if(StringUtils.isEmpty(ob)){
            return 0;
        }
        return Integer.parseInt(ob);

    }

    public Long ttl(String key) {
        return redisTemplate.getExpire(key);
    }

    @Bean
    @Primary
    public CacheManager redisCacheManager() {
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisTemplate.getConnectionFactory())
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(1)))
                .build();
        return cacheManager;
    }


    @Bean
    public CacheManager redisCacheManager5() {
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisTemplate.getConnectionFactory())
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)))
                .build();
        return cacheManager;
    }

}
